package com.iwown.sample4GApi.controller;


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.iwown.sample4GApi.bean.AppConstant;
import com.iwown.sample4GApi.calculation.EcgPreprocessor;
import com.iwown.sample4GApi.calculation.RriPreprocessor;
import com.iwown.sample4GApi.calculation.SleepPreprocessor;
import com.iwown.sample4GApi.calculation.Spo2Preprocessor;
import com.iwown.sample4GApi.service.HistoryDataParser;
import com.iwown.sample4GApi.service.IDataParser;
import com.iwown.sample4GApi.service.OldManDataParser;
import com.iwown.sample4GApi.utils.ByteUtils;
import com.iwown.sample4GApi.utils.MyHttpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * This api is for watch uploading health data, the route /pb/upload can't be changed
 * content-type : application/octet-stream
 */
@RestController
@EnableAutoConfiguration
@RequestMapping("/pb")
public class DeviceController {
    private static final Logger log = LoggerFactory.getLogger(DeviceController.class);
    private MyHttpUtils http_utils = new MyHttpUtils();
    private ByteUtils bytes_utils = new ByteUtils();
    @Qualifier("redis")
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @RequestMapping(value = "upload", method = RequestMethod.POST)
    private void uploadPBData(HttpServletRequest req, HttpServletResponse resp) {
        try {
            http_utils.printHeaders(req);

            int content_length = req.getContentLength();
            ServletInputStream instream = req.getInputStream();
            byte[] buffer = new byte[content_length];
            int offset = 0;
            int remain = content_length;
            while (true){
                int read_size = instream.read(buffer, offset, remain);
                if(read_size<=0){
                    break;
                }
                offset+=read_size;
                remain-=read_size;
            }
            instream.close();
            bytes_utils.bytesToHexString(buffer);
            offset = 0;
            //the first 15 bytes is deviceid/imei
            byte[] deviceId_bytes = new byte[15];
            System.arraycopy(buffer,offset,deviceId_bytes,0,15);
            String deviceId = new String(deviceId_bytes);
            log.info("deviceid: "+deviceId);
            offset += 15;
            //there maybe multiple data packages possible, need loop to parse all
            while (true){
                if(offset>content_length-9){
                    break;
                }
                //parse header part which contains 8 bytes
                byte[] prefix_bytes = new byte[2];//fixed to 0x4454
                byte[] length_bytes = new byte[2];//length of the payload
                byte[] crc_bytes = new byte[2];//crc check
            /*
            the opt number of the payload
            opt number:
            0x0A - device info/realtime step/gnss
            0x80 - history data, history health/ecg
            0x00 - device info
             */
                byte[] opt_bytes = new byte[2];
                System.arraycopy(buffer,offset,prefix_bytes,0,2);
                if(prefix_bytes[0] != 0x44 || prefix_bytes[1] != 0x54){
                    break;
                }
                offset += 2;
                System.arraycopy(buffer,offset,length_bytes,0,2);
                int length = bytes_utils.byteToPositiveInt(length_bytes[1])*0x100 + bytes_utils.byteToPositiveInt(length_bytes[0]);
                offset += 2;
                System.arraycopy(buffer,offset,crc_bytes,0,2);
                offset += 2;
                System.arraycopy(buffer,offset,opt_bytes,0,2);
                int opt = (opt_bytes[0] | opt_bytes[1]<<8) & 0x00FF;
                offset += 2;
                if(length<=0){
                    bytes_utils.bytesToHexString(length_bytes);
                    break;
                }

                //parse data part
                byte[] payload = new byte[length];
                System.arraycopy(buffer,offset,payload,0,length);
                offset += length;
                log.info(String.format("opt:%d",opt));
                IDataParser dataParser;
                if (opt == 0x0A) {
                    dataParser = new OldManDataParser(deviceId, redisTemplate);
                    dataParser.DataParse(payload);
                }
                else if(opt == 0x80){
                    dataParser = new HistoryDataParser(deviceId, redisTemplate);
                    dataParser.DataParse(payload);
                /*
				this is for sleep calculation, show how to do some preprocess to prepare data
				and then invoke the calculation api
			    */
                    SleepPreprocessor sleepProcessor = new SleepPreprocessor(deviceId, redisTemplate);
                    sleepProcessor.DataPrepare(payload);

                /*
				this is for ecg calculation, show how to do some preprocess to prepare data
				and then invoke the calculation api
			    */
                    EcgPreprocessor ecgProcessor = new EcgPreprocessor();
                    ecgProcessor.DataPrepare(payload);

                /*
				this is for af calculation, show how to do some preprocess to prepare data
				and then invoke the calculation api
			    */
                    RriPreprocessor rriProcessor = new RriPreprocessor();
                    rriProcessor.DataPrepare(payload);

                /*
				this is for continuous spo2 calculation, show how to do some preprocess to prepare data
				and then invoke the calculation api
			    */
                    Spo2Preprocessor spo2Processor = new Spo2Preprocessor();
                    spo2Processor.DataPrepare(payload);
                }
            }

            Map<String, List<Integer>> rriDataMap = HistoryDataParser.rri_data_map;
            if(rriDataMap.get(deviceId) != null){
                List<Integer> rriPacket = rriDataMap.get(deviceId);
                redisTemplate.opsForHash().put(AppConstant.ECG_PACKET + deviceId, "rriInfos", rriPacket);
                rriDataMap.remove(deviceId);
            }
            if (stringRedisTemplate.hasKey(AppConstant.DEVICE_CORE_EVA_INTERVAL_INFO + deviceId)) {
//                Object o = redisTemplate.opsForValue().get(AppConstant.DEVICE_CORE_EVA_INTERVAL_INFO + deviceId);
//                if(Objects.isNull(o)) {
//                    return;
//                }
//                JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(o)).getJSONObject("value");

                String imeiInfo = stringRedisTemplate.opsForValue().get(AppConstant.DEVICE_CORE_EVA_INTERVAL_INFO + deviceId);
                if (org.springframework.util.StringUtils.isEmpty(imeiInfo)) {
                    return;
                }
                //获得设备在系统的基本信息
                JSONObject jsonObject = JSON.parseObject(imeiInfo).getJSONObject("value");
                String userCode = jsonObject.getString("userCode");
                Long userId = jsonObject.getLong("userId");
                if(redisTemplate.opsForZSet().score(AppConstant.DEVICE_CORE_P3A_INTERVAL_ACQUISITION_QUEUE, userCode + "|" + deviceId + "|" + userId) == null) {
                    log.info("add device to queue: {}|{}|{}", userCode, deviceId, userId);
                    redisTemplate.opsForZSet().add(AppConstant.DEVICE_CORE_P3A_INTERVAL_ACQUISITION_QUEUE, userCode + "|" + deviceId + "|" + userId, System.currentTimeMillis() + 10000);
                }
            }
            //reponse content is text/plain, binary
            byte[] data = new byte[]{0x00};
            resp.getOutputStream().write(data);
            resp.getOutputStream().close();
        } catch (Exception e) {
            log.error("uploadPBData error", e);
        }

    }

}
